#pragma once

#include <engine.core/noncopyable.hpp>
#include <engine.core/math.hpp>
#include <engine.core/info.hpp>

#include "exports.hpp"




namespace Engine
{
	namespace Video
	{
		/**
		 * This class represents a device-independent Camera.
		 * It handles stuff like creating view-matrices, etc...
		 * and offers them so a device may use it to render the scene.
		 */
		class ENGINE_VIDEO_API Camera : public noncopyable
		{
		public:

			Camera(const EngineInfo& info);
			virtual ~Camera();

			/**
			 * Get the position of the Camera.
			 *
			 * @return   the position in world space
			 */
			float3 position() const;

			/**
			 * Set the position of the Camera.
			 *
			 * @param pos   the position in world space
			 */
			void setPosition(float3 pos);



			/**
			 * Get the look at point of the Camera.
			 *
			 * @return   the position of the point in world space
			 */
			float3 lookAt() const;

			/**
			 * Set the look at point of the Camera.
			 *
			 * @param pos   the position of the point in world space
			 */
			void setLookAt(float3 pos);



			/**
			 * Get the clipping planes of the Camera.
			 *
			 * @return   2-dimensional vector, where x represents the near plane's distance to the camera, and y the far one's
			 */
			float2 clippingPlanes() const;

			/**
			 * set the clipping planes of the Camera.
			 *
			 * @param planes   2-dimensional vector, where x represents the near plane's distance to the camera, and y the far one's
			 */
			void setClippingPlanes(const float2 &planes);



			/**
			 * Get the current view matrix.
			 */
			float4x4 view() const;

			/**
			* Get the view matrix from the previous frame.
			*/
			float4x4 previousView() const;



			/**
			 * Get the current projection matrix.
			 */
			float4x4 projection() const;

			/**
			 * Get the projection matrix from the previous frame.
			 */
			float4x4 previousProjection() const;



			/**
			 * Get the current view-projection matrix.
			 */
			float4x4 viewProjection() const;

			/**
			 * Get the view-projection matrix from the previous frame.
			 */
			float4x4 previousViewProjection() const;



			/**
			 * Get the current inverse view-projection matrix.
			 */
			float4x4 viewProjectionInverse() const;

			/**
			 * Get the inverse view-projection matrix from the previous frame.
			 */
			float4x4 previousViewProjectionInverse() const;



			/**
			 * Project a vector in world space into screen space.
			 */
			float3 project(const float3 &worldPoint) const;

			/**
			 * Unproject a vector in screen space into world space coordines.
			 */
			float3 unproject(const float3 &screenPoint) const;



			/**
			 * Test if the given screen space point is valid or not.
			 * Overloaded for float2 in case the z-component is not
			 * of interest (so one can test if the mouse is actually above
			 * the window or not).
			 */
			bool isValid(const float2 &screenPoint) const;

			/**
			 * Test if the given screen space point is valid or not.
			 */
			bool isValid(const float3 &screenPoint) const;



			/**
			 * Calculate the view matrix based on the current
			 * position and look at.
			 */
			void calculateViewMatrix();



			/**
			 * Updates the Camera.
			 */
			void update();

			/**
			 * Resizes the Camera (updates view matrices).
			 */
			void resize(uint32 screenWidth, uint32 screenHeight);

		private:

			// Position of the camera in world space
			float3     m_position;
			float3     m_lookAt;

			float4x4   m_viewProjectionInverse;
			float4x4   m_viewProjection;
			float4x4   m_projection;
			float4x4   m_view;

			float4x4   m_previousViewProjectionInverse;
			float4x4   m_previousViewProjection;
			float4x4   m_previousProjection;
			float4x4   m_previousView;

			float      m_fNearPlane;
			float      m_fFarPlane;
			float      m_fAspect;
			float      m_fFOV;
		};
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
